﻿/*****************************************************************************
RenderProfiler.StartProfilter(5,
	() =>
	{
		this.testButton.IsEnabled = false;
		this.Settings.BeginTimer();
	},
	() =>
	{
		ScrollBar.LineDownCommand.Execute(null, Keyboard.FocusedElement);
	},
	(t) =>
	{
		this.Settings.StopTimer("Line down 5 times: ");
		this.testButton.IsEnabled = true;
	});
*****************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Media;
using System.Windows;

namespace GrapeCity.Windows.Diagnostics
{
    public class RenderProfiler : DependencyObject
    {
        private RenderProfiler(int loop, Action initilize, Action step, Action<long> completed)
        {
            this.Loop = loop;
            this.Initialize = initilize;
            this.Step = step;
            this.Completed = completed;
        }

        private int Loop { get; set; }
        private Action Initialize { get; set; }
        private Action Step { get; set; }
        private Action<long> Completed { get; set; }

        int _renderCounter = 1;
        Stopwatch _stopwatch = null;

        private static List<RenderProfiler> _aliveProfiler = new List<RenderProfiler>();

        public static void StartProfilter(int loop
            , Action initializeCallback
            , Action stepCallback
            , Action<long> completeCallback)
        {
            RenderProfiler profiler = null;
            profiler = new RenderProfiler(loop
                , initializeCallback
                , stepCallback
                , (result) => {
                    completeCallback(result);
                    _aliveProfiler.Remove(profiler);
                });
            _aliveProfiler.Add(profiler);
            profiler.BeginTest();
        }

        private void BeginTest()
        {
            this.Initialize();

            this.HookMediaContextRenderCompleteEvent();

            _stopwatch = Stopwatch.StartNew();
            this._renderCounter = 0;

            //this.Dispatcher.BeginInvoke(new Action(BackupTrigger), System.Windows.Threading.DispatcherPriority.ApplicationIdle);
            this.TriggerRender();
        }
        private void BackupTrigger()
        {
            if (_renderCounter <= Loop)
            {
                this.TriggerRender();
                this.Dispatcher.BeginInvoke(new Action(BackupTrigger), System.Windows.Threading.DispatcherPriority.ApplicationIdle);
            }
        }
           
        private void EndTest()
        {
            _stopwatch.Stop();
            this.UnHookMediaContextRenderCompleteEvent();

            this.Completed(_stopwatch.ElapsedMilliseconds);
        }
        private void HookMediaContextRenderCompleteEvent()
        {
            Assembly presentationCoreAssebly = Assembly.GetAssembly(typeof(DrawingContext));
            Type typeOfMediaContext = presentationCoreAssebly.GetType("System.Windows.Media.MediaContext", true, false);
            PropertyInfo currentMediaContextPropertyInfo = typeOfMediaContext.GetProperty("CurrentMediaContext", BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Static);
            EventInfo renderCompletedEventInfo = typeOfMediaContext.GetEvent("RenderComplete", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty);
            MethodInfo renderCompletedEventAddMethod = renderCompletedEventInfo.GetAddMethod(true);
            renderCompletedEventAddMethod.Invoke(currentMediaContextPropertyInfo.GetValue(null, null), new object[] { Delegate.CreateDelegate(typeof(EventHandler), this, "ProcessRenderComplete") });
        }
        private void UnHookMediaContextRenderCompleteEvent()
        {
            Assembly presentationCoreAssebly = Assembly.GetAssembly(typeof(DrawingContext));
            Type typeOfMediaContext = presentationCoreAssebly.GetType("System.Windows.Media.MediaContext", true, false);
            PropertyInfo currentMediaContextPropertyInfo = typeOfMediaContext.GetProperty("CurrentMediaContext", BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Static);
            EventInfo renderCompletedEventInfo = typeOfMediaContext.GetEvent("RenderComplete", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty);
            MethodInfo renderCompletedEventRemoveMethod = renderCompletedEventInfo.GetRemoveMethod(true);
            renderCompletedEventRemoveMethod.Invoke(currentMediaContextPropertyInfo.GetValue(null, null), new object[] { Delegate.CreateDelegate(typeof(EventHandler), this, "ProcessRenderComplete") });
        }
        private void ProcessRenderComplete(object sender, EventArgs e)
        {
            this.Dispatcher.BeginInvoke(new Action(TriggerRender), System.Windows.Threading.DispatcherPriority.Loaded);
        }

        private void TriggerRender()
        {
            if (this._renderCounter++ >= this.Loop)
            {
                this.EndTest();
                return;
            }
            this.Step();
        }
    }
}
